package battleship.view;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import battleship.Controller;
import battleship.Loop;
import battleship.PluginAI;
import battleship.PluginBackground;
import battleship.PluginEnd;
import battleship.PluginLoop;
import battleship.PluginSetup;
import battleship.Setup;
import battleship.boat.Boat;
import battleship.data.SaveLoader;
import battleship.data.SaveXML;
import battleship.field.Field;
import battleship.player.Player;

public class Console {

	public Console()
	{
		
		
	}
	
	public void loadSaves()
	{
		SaveLoader loader;
		try {
			loader = new SaveLoader();
			if(loader.LoadField())
			{
				
				boolean b_load = scanYesNo("Wil je je vorige spel laden?: "
						+ loader.getNames()[0]
						+ loader.getNames()[1]);

				if(b_load)
				{
					
				}
			}
			
			
		} catch (Exception e) {
			SaveXML.FILE.delete();
			System.err.println("Corrupte savefile deleted");
			return;
		
		}
		
	}
	
	public void start()
	{
		
		Setup.loadAIPlugins(); // need to load this to block certain names
		if(Controller.getController().getContinueAskPlayers())
		{
			Setup.setState(Setup.SETUPPLAYERS);
			askPlayers();
		}
		if(Controller.getController().getContinueAskSize())
		{
		askSize();
		}
		
		if(Controller.getController().getContinueSetupBoats())
		{
			Setup.setState(Setup.SETUP);
			Setup.loadSetupPlugins();
			setupBoats();
		}
		Setup.loadLoopPlugins();
		try {
			Setup.loadBackgroundPlugins();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Setup.setState(Setup.LOOP);
		
		loop();
	}
	
	public void askPlayers()
	{
		int players;
		System.out.printf("Welkom bij zeeslag!!\n");
		
		players = scanInt("Hoeveel spelers zijn er?", Setup.getMinplayers(), Setup.getMaxplayers());

		
		
		for (int player = 0; player < players; ++player)
		{
			System.out.printf("Speler%d: ", player + 1); // makes it clearer
			Setup.getPlayers()[player] = new Player(askName(), false);
		}
		for (int bot = 2; bot > players; --bot)
		{
			
			PluginAI ai = getNewBot();
			Setup.getPlayers()[bot -1] = new Player(ai.getName(), true);
			Setup.getPlayers()[bot -1].setAI(ai);
			
//			System.out.printf("Welke moeilijkheidsgraad moet deze bot hebben? ");
//			Scanner scanDiff = new Scanner(System.in);
//			String argument = scanDiff.nextLine();
//			
//			boolean name = false;
//			
//			while(!name)
//			{
//				if(argument.equals("help")) // call help to see bots
//				{
//					for(PluginAI plug : Controller.getPluginsAI())
//					{
//						displayHeader(plug.getName());
//						displayNotification(plug.getDescription());
//					}
//				}
//				else // set a specific bot and exit
//				{
//					for(PluginAI plug : Controller.getPluginsAI())
//					{
//						if(plug.getName().equals(argument))
//						{
//							name = true;
//						}
//					}
//				}
//			}
//			Setup.getPlayers()[bot - 1] = new Player(argument, true);
		}		
	}
	
	public String askName() //console way to ask name
	{
		String name = null; //have to initialize
		
		System.out.printf("Wat is uw naam? > ");
		boolean goodName = false;
		while(!goodName)
		{
			Scanner scanName = new Scanner(System.in);
			
			name = scanName.next();
			
			goodName = Setup.checkNames(name);
			
			if(!goodName) // print the error
			{
				System.out.printf("Deze naam is al in gebruik\nNaam > ");
			}
			System.out.println();
			
		}
		
		return name;
		
	}
	
	public void askSize()
	{
		boolean adjustField = scanYesNo("Wilt u de grote van het speelveld aanpassen?");
		int x;
		int y;
		
		if(adjustField)
		{
			
			boolean goodX = false;
			boolean goodY = false;
			
			while (!goodX)
			{
				x = scanInt("Welke waarde wilt u voor de breedte van het veld?", Field.MIN, Field.MAX);

				goodX = Setup.checkFieldParameters(x);
				if(goodX)
				{
					Field.setWidth(x);
				}
			}

			while(!goodY)
			{
				y = scanInt("Welke waarde wilt u voor de hoogte van het veld?", Field.MIN, Field.MAX);

				goodY = Setup.checkFieldParameters(y);
				if(goodY)
				{
					Field.setHeight(y);
				}
			}

			
		}
	}
	
	public void setupBoats(){
		
		String arguments;
		String[] splitArguments;
		String[] argsToTransmit;
		
		
		
		Player[] players = Setup.getPlayers();
		
		for (Player setupPlayer: players) {
			if (!setupPlayer.isBot()){  //If player is human

				boolean allAtOnce = false;
				PluginSetup lastUsedPlugin = null;
				System.out.printf("commandos:\n\n" +
						"functies\tprint alle mogelijke commandos\n" +
						"help\t\tprint help voor elke functie\n" +
				"help + functie\tprint help voor specifieke functie\n\n");



				for(int boatNo = 0; boatNo < setupPlayer.getField().getBoats().length; boatNo++){


					/* mention name of the boat being set*/
					if (!allAtOnce) // we dont want to print it while its auto setting up
					{
						System.out.printf("%s van %s kan nu worden ingesteld.\n", setupPlayer.getField().getBoats()[boatNo].getName(), setupPlayer.getName());
					}

					boolean goodExecution = false;

					int error;


					if(!allAtOnce || lastUsedPlugin == null) {
						do {
							System.out.printf("%s, %s > ", setupPlayer.getField().getBoats()[boatNo].getName(), setupPlayer.getName());
							Scanner scanArguments = new Scanner(System.in);
							arguments = scanArguments.nextLine();

							splitArguments = arguments.split(" ");

							List<String> shortenArray = new ArrayList<String>(Arrays.asList(splitArguments));
							shortenArray.remove(0);
							argsToTransmit = shortenArray.toArray(new String[shortenArray.size()]);

							if (splitArguments[0].equals("help")) //call help options
							{
								boolean printSpecificHelp = false;
								if (splitArguments.length > 1) {
									for(PluginSetup help : Controller.getPluginsSetup())
									{
										if(splitArguments[1].equals(help.setupGetFunction())) //check which help to call
										{
											printSpecificHelp = true; //set this so general help wont be printed
											// this will prevent looping the help
											this.printHelp(help);

										}
									}
								}
								if(!printSpecificHelp) //call this if no specific function was asked
								{
									for(PluginSetup help : Controller.getPluginsSetup())
									{

										printHelp(help);

									}
								}
							}
							else if(splitArguments[0].equals("functies"))
							{
								for (PluginSetup help : Controller.getPluginsSetup())
								{

									System.out.println(help.setupGetFunction());

								}
							}
							else
							{
								for (PluginSetup plug : Controller.getPluginsSetup())
								{
									if (splitArguments[0].equals(plug.setupGetFunction())) {
										error = plug.setupExecFunction(setupPlayer, setupPlayer.getField().getBoats()[boatNo], argsToTransmit);
										lastUsedPlugin = plug;
										goodExecution = plug.setupErrorHandler(error);
										allAtOnce = plug.allAtOnce();
									}
								}
							}
						} while (!goodExecution);
					}
					else
					{
						lastUsedPlugin.setupExecFunction(setupPlayer, setupPlayer.getField().getBoats()[boatNo], new String[0]); //doesnt need string anyway
					}
				}

				lastUsedPlugin.onFinish(setupPlayer);
			}
			else
			{
				setFieldBot(setupPlayer);
				System.out.println(setupPlayer.getName()
						+ " heeft zijn veld opgesteld");
			}
		}
	}


	public boolean scanYesNo(String sentence) {
		
		while(true)
		{
			String yesNo;
			Scanner scanYN = new Scanner(System.in);
			System.out.printf("%s [y/n] ", sentence);
			yesNo = scanYN.nextLine();
			
			if(yesNo.equalsIgnoreCase("y"))
			{
				return true;
			}
			else if(yesNo.equalsIgnoreCase("n"))
			{
				return false;
			}
			else
			{
				System.out.printf("\nU kunt enkel [y/n] ingeven.");
			}
			System.out.println();
		}
	}
	
	public int scanInt(String sentence,int min, int max)
	{
		while(true)
		{
			int returnValue;
			Scanner scanInt = new Scanner(System.in);
			try {
				System.out.printf("%s [%d-%d] ", sentence, min, max);
				returnValue = scanInt.nextInt();
				System.out.println();
				if(returnValue > max || returnValue < min)
				{
					throw new Exception();
				}
				return returnValue;
			} catch (Exception wrongInt)
			{
				System.out.printf("\nDeze waarde is ongeldig\n");
			}
		}
	}
	
	public void endGame()
	{
		for(PluginEnd endPlug : Controller.getPluginsEnd())
		{
			endPlug.onEnd();
		}
	}
	
	public void loop()
	{	
		
		System.out.printf("Het spel kan nu beginnen, %s mag eerst aanvallen\n", Setup.getPlayers()[0].getName());
		while(!Loop.getWin())
		{
			
			//TODO: convert to a more global class
			Player player2;
			Loop.increaseTurns();
			boolean correctStrike = false; //start as the error value
			int errorCode;
			
			player2 = Setup.getPlayer2(Loop.getLastTurn());
			if(!player2.isBot()) {

				do {
					String[] arguments;

					Scanner scanArgs = new Scanner(System.in);
					System.out.printf("\nattack! %s > ", player2.getName());
					arguments = scanArgs.nextLine().split(" ");
					List<String> listArguments = new ArrayList<String>(Arrays.asList(arguments));
					String[] parseArguments;
					listArguments.remove(0);

					parseArguments = listArguments.toArray(new String[listArguments.size()]);
					if(arguments[0].equals("help"))
					{
						if(arguments.length > 1)
						{
							for (PluginLoop help : Controller.getPluginsLoop())
							{
								if(arguments[1].equals(help.loopGetFunction()))
								{
									printHelp(help);
								}
							}
						}
						else
						{
							for(PluginLoop help : Controller.getPluginsLoop())
							{
								printHelp(help);
							}
						}
					}
					for (PluginLoop plug : Controller.getPluginsLoop()) {
						if(arguments[0].equals(plug.loopGetFunction())) {
							errorCode = plug.loopExecFunction(parseArguments, player2);
							correctStrike = plug.loopErrorHandler(errorCode);
						}

					}


				} while(!correctStrike);
				
				printBoard(false, player2.getField());
			}
			
			//bot attacks
			else
			{
				attackBot(player2);
				
				int x = player2.getAI().getX();
				int y = player2.getAI().getY();
				
				String strX = Setup.getPositionMap()[x];
				String strY = String.valueOf(y);
				
				System.out.println(player2.getName() + " heeft aangevallen op "
						+ strX
						+ strY);
				
				Player player = Setup.getPlayer2(player2);
				
				Controller.getController().printBoard(false, player.getField());
			}
			//clear screen
			for(int i = 0; i < 20; ++i)
			{
				System.out.println();
			}
			
			//declare winner + exit
			if(Loop.getWin(Loop.getLastTurn()))
			{
				System.out.println(Loop.getLastTurn().getName()
						+ " heeft het spel gewonnen");
				
				System.exit(0);
			}
			//backgrounded actions
			for(PluginBackground plug : Controller.getPluginsBackground())
			{
				plug.backGroundAction();
			}
		}

	}
	
	/**
	 * used to set the boats for the bot
	 * 
	 * @param bot the bot to setup
	 */
	public void setFieldBot(Player bot)
	{
		PluginAI ai = bot.getAI();
		PluginSetup setup = ai.getSetupAlgorithm();
		
		for (int boat = 0; boat < bot.getField().getBoats().length; ++boat)
		{
			setup.setupExecFunction(bot, bot.getField().getBoats()[boat], null);
		}
	}
	
	public void attackBot(Player bot)
	{
		bot.getAI().attack(bot);
	}

	public void displayNotification(String note)
	{
		System.out.println(note);
	}

	public void printHelp(PluginSetup help)
	{
		System.out.printf("\nFunctie: %s\tOpties: %s\n" +
				"------------------\n" +
				"%s\n" +
				"------------------\n",help.setupGetFunction(),help.setupGetOptions(), help.setupGetDescription());
	}
	
	public void printHelp(PluginLoop help)
	{
		System.out.printf("\nFunctie: %s\tOpties: %s\n" +
				"------------------\n" +
				"%s\n" +
				"------------------\n",help.loopGetFunction(),help.loopGetOptions(),help.loopGetDescription());
	}
	
	public void printBoard(boolean showall, Field field){
		int width = Field.getWidth();
		int height = Field.getHeight();
		
		final int EMPTY = 0;
		final int FILLED = 1;
		final int HIT = 2;
		final int MISS = 3;
		
		Boat[] boats = field.getBoats();
		
		for(int row = 0; row < height; row++){						//go through every row
			
			
			if (row == 0) // print numbers above the columns
			{
				System.out.printf("    "); // spaces for the first number
				for (int printNumber = 0; printNumber < width; printNumber++) {
					System.out.printf("%2d ", printNumber + 1);
				}
				System.out.println(); // next line
				
			}
			
			
			System.out.printf("%2s ", Setup.getPositionMap()[row]);
			for(int column = 0; column < width; column++){			//go through every column
				//show everything on the field
				
				byte fieldCode = EMPTY;
				for(int boatsNo = 0; boatsNo < field.getBoats().length; boatsNo++){
					if(boats[boatsNo].isHit(new int[]{row, column})){ //boat exists in this plain
						if(field.getField()[row][column]){
							//System.out.printf("|##");
							fieldCode = HIT;
						}
						else{
							//System.out.printf("|++");
							fieldCode = FILLED;
						}
					}
					else if(field.getField()[row][column] && fieldCode != 2){ 
						//System.out.printf("|--");
						fieldCode = MISS;
					}
				}
				if(fieldCode == HIT) {
					System.out.printf("|##");
				}
				else if(fieldCode == FILLED && showall){
					System.out.printf("|++");
				}
				else if(fieldCode == MISS){
					System.out.printf("|--");
				}
				else {
					System.out.printf("|..");
				}

			} 
			System.out.printf("|\n");//end of row
		}
	}

	public void displayHeader(String header) {
		System.out.printf("%s\n-----------------------------------------\n\n", header);
		
	}
	
	public void displayError(String error, PluginLoop plugin) {
		System.out.println("Error: " + plugin.loopGetFunction() + ": " + error);
	}
	
	/**
	 * Used to get the bot from the command line.
	 * Keeps looping untill a valid one is found.
	 * 
	 * @return the selected bot
	 */
	public PluginAI getNewBot()
	{
		System.out.println("Alle beschikbare bots:");
		for(PluginAI name : Controller.getPluginsAI()) {
			System.out.println(name.getName());
		}
		System.out.println("Geef in welke bot u wilt: ");
		
		while(true) {
			Scanner scanName = new Scanner(System.in);
			
			String possibleName = scanName.nextLine();
			
			for (PluginAI possibility : Controller.getPluginsAI())
			{
				if (possibleName.equals(possibility.getName())) return possibility;
			}
			System.out.println("Deze naam bestaat niet");
		}
	}
}
